From: Florian Müllner Date: Mon, 3 Sep 2018 23:41:55 +0000 (+0200) Subject: extensions-tool: Add 'install' command X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~832^2^2~11 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=a429fdbd081722f079b0452df37abc97f396ee5f;p=gnome-shell.git extensions-tool: Add 'install' command The ability to install unaudited extensions directly from a zip file can be useful for testing and code review, so implement a corresponding command that complements the previously added 'pack' command. https://gitlab.gnome.org/GNOME/gnome-shell/issues/1234 --- diff --git a/src/extensions-tool/command-install.c b/src/extensions-tool/command-install.c new file mode 100644 index 000000000..58f62bb86 --- /dev/null +++ b/src/extensions-tool/command-install.c @@ -0,0 +1,207 @@ +/* command-install.c + * + * Copyright 2018 Florian Müllner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include + +#include +#include + +#include "commands.h" +#include "common.h" +#include "config.h" + +static JsonObject * +load_metadata (GFile *dir, + GError **error) +{ + g_autoptr (JsonParser) parser = NULL; + g_autoptr (GInputStream) stream = NULL; + g_autoptr (GFile) file = NULL; + + file = g_file_get_child (dir, "metadata.json"); + stream = G_INPUT_STREAM (g_file_read (file, NULL, error)); + if (stream == NULL) + return NULL; + + parser = json_parser_new_immutable (); + if (!json_parser_load_from_stream (parser, stream, NULL, error)) + return NULL; + + return json_node_dup_object (json_parser_get_root (parser)); +} + +static void +on_error (AutoarExtractor *extractor, + GError *error, + gpointer data) +{ + *((GError **)data) = g_error_copy (error); +} + +static GFile * +on_decide_destination (AutoarExtractor *extractor, + GFile *dest, + GList *files, + gpointer data) +{ + g_autofree char *dest_path = NULL; + GFile *new_dest; + int copy = 1; + + dest_path = g_file_get_path (dest); + new_dest = g_object_ref (dest); + + while (g_file_query_exists (new_dest, NULL)) + { + g_autofree char *new_path = g_strdup_printf ("%s (%d)", dest_path, copy); + + g_object_unref (new_dest); + new_dest = g_file_new_for_path (new_path); + + copy++; + } + + *((GFile **)data) = g_object_ref (new_dest); + + return new_dest; +} + +static int +install_extension (const char *bundle, + gboolean force) +{ + g_autoptr (AutoarExtractor) extractor = NULL; + g_autoptr (JsonObject) metadata = NULL; + g_autoptr (GFile) cachedir = NULL; + g_autoptr (GFile) tmpdir = NULL; + g_autoptr (GFile) src = NULL; + g_autoptr (GFile) dst = NULL; + g_autoptr (GFile) dstdir = NULL; + g_autoptr (GError) error = NULL; + g_autofree char *cwd = NULL; + const char *uuid; + + cwd = g_get_current_dir (); + src = g_file_new_for_commandline_arg_and_cwd (bundle, cwd); + cachedir = g_file_new_for_path (g_get_user_cache_dir ()); + + extractor = autoar_extractor_new (src, cachedir); + + g_signal_connect (extractor, "error", G_CALLBACK (on_error), &error); + g_signal_connect (extractor, "decide-destination", G_CALLBACK (on_decide_destination), &tmpdir); + + autoar_extractor_start (extractor, NULL); + + if (error != NULL) + goto err; + + metadata = load_metadata (tmpdir, &error); + if (metadata == NULL) + goto err; + + dstdir = g_file_new_build_filename (g_get_user_data_dir (), + "gnome-shell", "extensions", NULL); + + if (!g_file_make_directory_with_parents (dstdir, NULL, &error)) + goto err; + + uuid = json_object_get_string_member (metadata, "uuid"); + dst = g_file_get_child (dstdir, uuid); + + if (g_file_query_exists (dst, NULL)) + { + if (!force) + { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_EXISTS, + "%s exists and --force was not specified", uuid); + goto err; + } + else if (!file_delete_recursively (dst, &error)) + { + goto err; + } + } + + if (!g_file_move (tmpdir, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, &error)) + goto err; + + return 0; + +err: + if (error != NULL) + g_printerr ("%s\n", error->message); + + if (tmpdir != NULL) + file_delete_recursively (tmpdir, NULL); + + return 2; +} + +int +handle_install (int argc, char *argv[], gboolean do_help) +{ + g_autoptr (GOptionContext) context = NULL; + g_autoptr (GError) error = NULL; + g_auto (GStrv) filenames = NULL; + gboolean force = FALSE; + GOptionEntry entries[] = { + { .long_name = "force", .short_name = 'f', + .arg = G_OPTION_ARG_NONE, .arg_data = &force, + .description = _("Overwrite an existing extension") }, + { .long_name = G_OPTION_REMAINING, + .arg_description =_("EXTENSION_BUNDLE"), + .arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = &filenames }, + { NULL } + }; + + g_set_prgname ("gnome-extensions install"); + + context = g_option_context_new (NULL); + g_option_context_set_help_enabled (context, FALSE); + g_option_context_set_summary (context, _("Install an extension bundle")); + g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + + if (do_help) + { + show_help (context, NULL); + return 0; + } + + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + show_help (context, error->message); + return 1; + } + + if (filenames == NULL) + { + show_help (context, _("No extension bundle specified")); + return 1; + } + + if (g_strv_length (filenames) > 1) + { + show_help (context, _("More than one extension bundle specified")); + return 1; + } + + return install_extension (*filenames, force); +} diff --git a/src/extensions-tool/command-pack.c b/src/extensions-tool/command-pack.c index 28c39adb7..d98e8fa4f 100644 --- a/src/extensions-tool/command-pack.c +++ b/src/extensions-tool/command-pack.c @@ -38,28 +38,6 @@ typedef struct _ExtensionPack { static void extension_pack_free (ExtensionPack *); G_DEFINE_AUTOPTR_CLEANUP_FUNC (ExtensionPack, extension_pack_free); -static void -delete_recursively (GFile *file) -{ - g_autoptr (GFileEnumerator) file_enum = NULL; - GFile *child; - - file_enum = g_file_enumerate_children (file, NULL, 0, NULL, NULL); - if (file_enum) - while (TRUE) - { - if (!g_file_enumerator_iterate (file_enum, NULL, &child, NULL, NULL)) - return; - - if (child == NULL) - break; - - delete_recursively (child); - } - - g_file_delete (file, NULL, NULL); -} - static ExtensionPack * extension_pack_new (const char *srcdir) { @@ -74,7 +52,7 @@ static void extension_pack_free (ExtensionPack *pack) { if (pack->tmpdir) - delete_recursively (pack->tmpdir); + file_delete_recursively (pack->tmpdir, NULL); g_clear_pointer (&pack->files, g_hash_table_destroy); g_clear_pointer (&pack->metadata, json_object_unref); diff --git a/src/extensions-tool/commands.h b/src/extensions-tool/commands.h index 677998fc7..247a14434 100644 --- a/src/extensions-tool/commands.h +++ b/src/extensions-tool/commands.h @@ -30,5 +30,6 @@ int handle_list (int argc, char *argv[], gboolean do_help); int handle_info (int argc, char *argv[], gboolean do_help); int handle_create (int argc, char *argv[], gboolean do_help); int handle_pack (int argc, char *argv[], gboolean do_help); +int handle_install (int argc, char *argv[], gboolean do_help); G_END_DECLS diff --git a/src/extensions-tool/common.h b/src/extensions-tool/common.h index 849c9caab..860813a07 100644 --- a/src/extensions-tool/common.h +++ b/src/extensions-tool/common.h @@ -54,4 +54,7 @@ void print_extension_info (GVariantDict *info, GDBusProxy *get_shell_proxy (GError **error); GSettings *get_shell_settings (void); +gboolean file_delete_recursively (GFile *file, + GError **error); + G_END_DECLS diff --git a/src/extensions-tool/completion/bash/gnome-extensions b/src/extensions-tool/completion/bash/gnome-extensions index bc4816afa..73b594281 100644 --- a/src/extensions-tool/completion/bash/gnome-extensions +++ b/src/extensions-tool/completion/bash/gnome-extensions @@ -5,7 +5,7 @@ ################################################################################ __gnome_extensions() { - local commands="version enable disable info show list create pack" + local commands="version enable disable info install show list create pack" local COMMAND=${COMP_WORDS[1]} _init_completion -s || return @@ -54,6 +54,13 @@ __gnome_extensions() { ;; esac ;; + install) + if [[ $cur != -* ]] + then + _filedir zip + return 0 + fi + ;; esac # Stop if we are currently waiting for an option value diff --git a/src/extensions-tool/main.c b/src/extensions-tool/main.c index 6a59079fb..467f49805 100644 --- a/src/extensions-tool/main.c +++ b/src/extensions-tool/main.c @@ -126,6 +126,31 @@ print_extension_info (GVariantDict *info, g_print (" %s: %s\n", _("State"), extension_state_to_string (state)); } +gboolean +file_delete_recursively (GFile *file, + GError **error) +{ + g_autoptr (GFileEnumerator) file_enum = NULL; + GFile *child; + + file_enum = g_file_enumerate_children (file, NULL, 0, NULL, NULL); + if (file_enum) + while (TRUE) + { + if (!g_file_enumerator_iterate (file_enum, NULL, &child, NULL, error)) + return FALSE; + + if (child == NULL) + break; + + if (!file_delete_recursively (child, error)) + return FALSE; + } + + return g_file_delete (file, NULL, error); +} + + static int handle_version (int argc, char *argv[], gboolean do_help) { @@ -163,6 +188,7 @@ usage (void) g_printerr (" show %s\n", _("Show extension info")); g_printerr (" create %s\n", _("Create extension")); g_printerr (" pack %s\n", _("Package extension")); + g_printerr (" install %s\n", _("Install extension bundle")); g_printerr ("\n"); g_printerr (_("Use %s to get detailed help.\n"), "“gnome-extensions help COMMAND”"); } @@ -230,6 +256,8 @@ main (int argc, char *argv[]) return handle_create (argc, argv, do_help); else if (g_str_equal (command, "pack")) return handle_pack (argc, argv, do_help); + else if (g_str_equal (command, "install")) + return handle_install (argc, argv, do_help); else usage (); diff --git a/src/extensions-tool/man/gnome-extensions.txt b/src/extensions-tool/man/gnome-extensions.txt index c29016d9d..c362e4d0a 100644 --- a/src/extensions-tool/man/gnome-extensions.txt +++ b/src/extensions-tool/man/gnome-extensions.txt @@ -29,6 +29,8 @@ SYNOPSIS *gnome-extensions* pack ['OPTION'...] +*gnome-extensions* install ['OPTION'...] 'PACK' + DESCRIPTION ----------- *gnome-extensions* is a utility that makes some common GNOME extensions @@ -147,6 +149,22 @@ the current directory otherwise. *--out-dir*='DIRECTORY'::: The directory where the pack should be created +*install* ['OPTION'...] 'PACK':: +Installs an extension from the bundle 'PACK'. ++ +The command unpacks the extension files and moves them to +the expected location in the user's *$HOME*, so that it +will be loaded in the next session. ++ +It is mainly intended for testing, not as a replacement for +GNOME Software or the extension website. As extensions have +privileged access to the user's session, it is advised to +never load extensions from untrusted sources without carefully +reviewing their content. ++ +.Options + *--force*::: + Override an existing extension EXIT STATUS ----------- diff --git a/src/extensions-tool/meson-src.build b/src/extensions-tool/meson-src.build index 60834f6d3..7af5365e4 100644 --- a/src/extensions-tool/meson-src.build +++ b/src/extensions-tool/meson-src.build @@ -4,6 +4,7 @@ sources = [ 'command-disable.c', 'command-enable.c', 'command-info.c', + 'command-install.c', 'command-list.c', 'command-pack.c', 'common.h', diff --git a/src/extensions-tool/meson.build b/src/extensions-tool/meson.build index 2528dfd52..390227490 100644 --- a/src/extensions-tool/meson.build +++ b/src/extensions-tool/meson.build @@ -13,6 +13,7 @@ sources = [ 'command-disable.c', 'command-enable.c', 'command-info.c', + 'command-install.c', 'command-list.c', 'command-pack.c', 'main.c'